<?php
error_reporting(E_ALL & ~(E_NOTICE | E_WARNING));

class Server
{
    protected $sock = null;

    protected $protocol = null;
    protected $host = null;
    protected $port = null;
    protected $dsn = null;

    public function __construct($dsn = 'localhost:8080')
    {
        $count = preg_match('#((?<protocol>\w{1,})\:\/\/|)(?<host>\w{1,})\:(?<port>[0-9]{1,})#', $dsn, $match);
        if ($count) {
            $this->protocol = $match['protocol'] ?: 'tcp';
            $this->host = $match['host'];
            $this->port = $match['port'];
            $this->dsn = $this->protocol . '://' . $this->host . ':' . $this->port;
        } else {
            echo "dsn error!";
        }

        $this->sock = stream_socket_server($this->dsn, $errno, $errstr);
        if (!$this->sock) {
            echo $errstr;
        }

        stream_set_blocking($this->sock, 1);
    }

    protected $onAccept = null;

    public function onAccept(Closure $onConnect = null)
    {
        is_callable($onConnect) && $this->onAccept = $onConnect;
        return $this;
    }

    public function run()
    {
        while (true) {
            if ($client_sock = stream_socket_accept($this->sock)) {
                $on_accept = $this->onAccept;
                call_user_func($on_accept, $client_sock);
                stream_socket_shutdown($client_sock, STREAM_SHUT_RDWR);
            }
        }
    }
}

class Pipe
{
    private $buffer_size = 1024;
    const READ_BUFFER = 0b1;
    const WRITE_BUFFER = 0b10;
    protected $read_buffer = "";
    protected $write_buffer = "";
    protected $buffer_able = false;

    public function bufferEnable()
    {
        $this->buffer_able = true;
    }

    public function bufferDisable()
    {
        $this->buffer_able = false;
    }

    public function clearBuffer($clear_part = self::READ_BUFFER | self::WRITE_BUFFER)
    {
        foreach (['read_buffer' => self::READ_BUFFER, 'write_buffer' => self::WRITE_BUFFER] as $name => $pattern) {
            if ($clear_part & $pattern) {
                $this->$name = "";
            }
        }
    }

    protected $sock = null;

    public function __construct($sock)
    {
        $this->sock = $sock;
    }

    public function write($data = null)
    {
        fwrite($this->sock, $data, mb_strlen($data));
    }

    public function readLine()
    {
        $remote_data = fgets($this->sock, 1024);
        return trim($remote_data);
    }
}

(new Server())->onAccept(function ($client_sock) {
    $pipe = new Pipe($client_sock);
    while (true) {
        if ($client_data = $pipe->readLine()) {
            echo $client_data, "\r\n";
            $pipe->write("get it[ {$client_data} ]!\r\n");
        }
    }
})->run();
